/*
 * %W% %E%
 *
 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.java.swing.plaf.windows;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.plaf.basic.*;
import javax.swing.*;
import javax.swing.plaf.TextUI;
import javax.swing.plaf.UIResource;
import javax.swing.text.*;

/**
 * Windows text rendering.
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases.  The current serialization support is appropriate
 * for short term storage or RMI between applications running the same
 * version of Swing.  A future release of Swing will provide support for
 * long term persistence.
 */
public abstract class WindowsTextUI extends BasicTextUI {
    /**
     * Creates the object to use for a caret.  By default an
     * instance of WindowsCaret is created.  This method
     * can be redefined to provide something else that implements
     * the InputPosition interface or a subclass of DefaultCaret.
     *
     * @return the caret object
     */
    protected Caret createCaret() {
        return new WindowsCaret();
    }

    /* public */
    static LayeredHighlighter.LayerPainter WindowsPainter = new WindowsHighlightPainter(null);

    /* public */
    static class WindowsCaret extends DefaultCaret
		     implements UIResource {
	/**
	 * Gets the painter for the Highlighter.
	 *
	 * @return the painter
	 */
	protected Highlighter.HighlightPainter getSelectionPainter() {
	    return WindowsTextUI.WindowsPainter;
	}
    }

    /* public */
    static class WindowsHighlightPainter extends
		     DefaultHighlighter.DefaultHighlightPainter {
	WindowsHighlightPainter(Color c) {
	    super(c);
	}

	// --- HighlightPainter methods ---------------------------------------

        /**
         * Paints a highlight.
         *
         * @param g the graphics context
         * @param offs0 the starting model offset >= 0
         * @param offs1 the ending model offset >= offs1
         * @param bounds the bounding box for the highlight
         * @param c the editor
         */
        public void paint(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c) {
	    Rectangle alloc = bounds.getBounds();
	    try {
		// --- determine locations ---
		TextUI mapper = c.getUI();
		Rectangle p0 = mapper.modelToView(c, offs0);
		Rectangle p1 = mapper.modelToView(c, offs1);

		// --- render ---
		Color color = getColor();

		if (color == null) {
		    g.setColor(c.getSelectionColor());
		}
		else {
		    g.setColor(color);
		}
		boolean firstIsDot = false;
		boolean secondIsDot = false;
		if (c.isEditable()) {
		    int dot = c.getCaretPosition();
		    firstIsDot = (offs0 == dot);
		    secondIsDot = (offs1 == dot);
		}
		if (p0.y == p1.y) {
		    // same line, render a rectangle
		    Rectangle r = p0.union(p1);
		    if (r.width > 0) {
			if (firstIsDot) {
			    r.x++;
			    r.width--;
			}
			else if (secondIsDot) {
			    r.width--;
			}
		    }
		    g.fillRect(r.x, r.y, r.width, r.height);
		} else {
		    // different lines
		    int p0ToMarginWidth = alloc.x + alloc.width - p0.x;
		    if (firstIsDot && p0ToMarginWidth > 0) {
			p0.x++;
			p0ToMarginWidth--;
		    }
		    g.fillRect(p0.x, p0.y, p0ToMarginWidth, p0.height);
		    if ((p0.y + p0.height) != p1.y) {
			g.fillRect(alloc.x, p0.y + p0.height, alloc.width, 
				   p1.y - (p0.y + p0.height));
		    }
		    if (secondIsDot && p1.x > alloc.x) {
			p1.x--;
		    }
		    g.fillRect(alloc.x, p1.y, (p1.x - alloc.x), p1.height);
		}
	    } catch (BadLocationException e) {
		// can't render
	    }
	}

	// --- LayerPainter methods ----------------------------
        /**
         * Paints a portion of a highlight.
         *
         * @param g the graphics context
         * @param offs0 the starting model offset >= 0
         * @param offs1 the ending model offset >= offs1
         * @param bounds the bounding box of the view, which is not
	 *        necessarily the region to paint.
         * @param c the editor
	 * @param view View painting for
	 * @return region drawing occured in
         */
	public Shape paintLayer(Graphics g, int offs0, int offs1,
				Shape bounds, JTextComponent c, View view) {
	    Color color = getColor();

	    if (color == null) {
		g.setColor(c.getSelectionColor());
	    }
	    else {
		g.setColor(color);
	    }
	    boolean firstIsDot = false;
	    boolean secondIsDot = false;
	    if (c.isEditable()) {
		int dot = c.getCaretPosition();
		firstIsDot = (offs0 == dot);
		secondIsDot = (offs1 == dot);
	    }
	    if (offs0 == view.getStartOffset() &&
		offs1 == view.getEndOffset()) {
		// Contained in view, can just use bounds.
		Rectangle alloc;
		if (bounds instanceof Rectangle) {
		    alloc = (Rectangle)bounds;
		}
		else {
		    alloc = bounds.getBounds();
		}
		if (firstIsDot && alloc.width > 0) {
		    g.fillRect(alloc.x + 1, alloc.y, alloc.width - 1,
			       alloc.height);
		}
		else if (secondIsDot && alloc.width > 0) {
		    g.fillRect(alloc.x, alloc.y, alloc.width - 1,
			       alloc.height);
		}
		else {
		    g.fillRect(alloc.x, alloc.y, alloc.width, alloc.height);
		}
		return alloc;
	    }
	    else {
		// Should only render part of View.
		try {
		    // --- determine locations ---
                    Shape shape = view.modelToView(offs0, Position.Bias.Forward,
                                                   offs1,Position.Bias.Backward,
                                                   bounds);
                    Rectangle r = (shape instanceof Rectangle) ?
                                  (Rectangle)shape : shape.getBounds();
		    if (firstIsDot && r.width > 0) {
			g.fillRect(r.x + 1, r.y, r.width - 1, r.height);
		    }
		    else if (secondIsDot && r.width > 0) {
			g.fillRect(r.x, r.y, r.width - 1, r.height);
		    }
		    else {
			g.fillRect(r.x, r.y, r.width, r.height);
		    }
                    return r;
		} catch (BadLocationException e) {
		    // can't render
		}
	    }
	    // Only if exception
	    return null;
	}

    }

}

